/**
 * Similarity Search 2012
 * Binary Branch Filter for Tree Edit Distance
 * @author Peter Kosa
 */
package treeedit;

import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map.Entry;

/**
 * @author Pepe
 */
public class Main {
	/**
	 * Main entry point to the application. Serves as a facade connecting all
	 * important functionality together. Provides UI for user interaction.
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		// default
		if (args == null || args.length == 0) {
			printUsage();
			throw new IllegalArgumentException("\nProvide more arguments.");
		}

		// file paths
		String input_file = "";
		String input_file2 = "";
		String output_file = "";

		try {
			if (args[0].equals("-h") || args[0].equals("--help")) {
				printUsage();
				return;
			} else if (args[0].equals("-g") || args[0].equals("--gen")) { // --gen
																			// option
				System.out.println("Generate tree...");
				int fanout = -1;
				int layers = -1;
				int label_length = -1;
				int number_of_trees = -1;
				// get all args
				for (int i = 1; i < args.length; i++) {
					if (args[i].equals("-o")) {
						output_file = args[++i];
					} else if (args[i].equals("-f")) {
						fanout = Integer.parseInt(args[++i]);
					} else if (args[i].equals("-l")) {
						layers = Integer.parseInt(args[++i]);
					} else if (args[i].equals("-s")) {
						label_length = Integer.parseInt(args[++i]);
					} else if (args[i].equals("-n")) {
						number_of_trees = Integer.parseInt(args[++i]);
					}
				}
				// not all args supplied
				if (fanout == -1 || layers == -1 || label_length == -1
						|| number_of_trees == -1 || output_file == "") {
					printUsage();
					throw new IllegalArgumentException(
							"Provide more arguments (or correct existing ones).");
				} else {
					Hashtable<Integer, Tree> ht = new Hashtable<Integer, Tree>();
					for (int i = 1; i <= number_of_trees; i++) {
						ht.put(i, RandomTreeGenerator.getFullRandomTree(layers, fanout,
								label_length));
					}
					Utils.saveFileToDisk(output_file, BZAddressParser.getForestString(ht));
					System.out.println("Generate tree completed.");
				}
			} else if (args[0].equals("-r") || args[0].equals("--rand")) { // --rand
																			// option
				System.out.println("Randomize tree...");
				int chance = -1;
				// get all args
				for (int i = 1; i < args.length; i++) {
					if (args[i].equals("-o")) {
						output_file = args[++i];
					} else if (args[i].equals("-i")) {
						input_file = args[++i];
					} else if (args[i].equals("-c")) {
						chance = Integer.parseInt(args[++i]);
					}
				}
				// not all args supplied
				if (chance == -1 || input_file == "" || output_file == "") {
					printUsage();
					throw new IllegalArgumentException(
							"Provide more arguments (or correct existing ones).");
				} else {
					Hashtable<Integer, Tree> ht = BZAddressParser.parse(input_file);
					Hashtable<Integer, Tree> ht2 = new Hashtable<Integer, Tree>();
					for (Entry<Integer, Tree> entry : ht.entrySet()) {
						ht2.put(entry.getKey(), RandomTreeGenerator.randomizeTree(
								entry.getValue(), chance));
					}
					Utils.saveFileToDisk(output_file,
							BZAddressParser.getForestString(ht2));
					System.out.println("Randomize tree completed.");
				}
			} else if (args[0].equals("-t") || args[0].equals("--ted")) { // --ted
																			// option
				System.out.println("Running TED...");
				// get all args
				for (int i = 1; i < args.length; i++) {
					if (args[i].equals("-i")) {
						input_file = args[++i];
						input_file2 = args[++i];
					} else if (args[i].equals("-o")) {
						output_file = args[++i];
					}
				}

				// not all args supplied
				if (input_file == "" || input_file2 == "") {
					printUsage();
					throw new IllegalArgumentException(
							"Provide more arguments (or correct existing ones).");
				} else {
					Hashtable<?, ?> resultTable = null;
					Hashtable<Integer, Tree> treetable1 = BZAddressParser
							.parse(input_file);
					Hashtable<Integer, Tree> treetable2 = BZAddressParser
							.parse(input_file2);
					resultTable = compareHashtablesWithTreesTED(treetable1, treetable2);
					// output
					String rts = Utils.getHashtableToString(resultTable);
					if (output_file == "") {
						System.out.println(rts);
					} else {
						Utils.saveFileToDisk(output_file, rts);
					}
					System.out.println("Running TED completed.");
				}
			} else if (args[0].equals("-b") || args[0].equals("--bbf")) { // --bbf
																			// option
				System.out.println("Running TED with BBF...");
				double treshold = Double.MIN_VALUE;
				// get all args
				for (int i = 1; i < args.length; i++) {
					if (args[i].equals("-i")) {
						input_file = args[++i];
						input_file2 = args[++i];
					} else if (args[i].equals("-t")) {
						treshold = Double.parseDouble(args[++i]);
					} else if (args[i].equals("-o")) {
						output_file = args[++i];
					}
				}

				// not all args supplied
				if (input_file == "" || input_file2 == "" || treshold == Double.MIN_VALUE) {
					printUsage();
					throw new IllegalArgumentException(
							"Provide more arguments (or correct existing ones).");
				} else {
					Hashtable<?, ?> resultTable = null;
					Hashtable<Integer, Tree> treetable1 = BZAddressParser
							.parse(input_file);
					Hashtable<Integer, Tree> treetable2 = BZAddressParser
							.parse(input_file2);
					resultTable = compareHashtablesWithTreesBBF(treetable1, treetable2,
							treshold);
					// output
					String rts = Utils.getHashtableToString(resultTable);
					if (output_file == "") {
						System.out.println(rts);
					} else {
						Utils.saveFileToDisk(output_file, rts);
					}
					System.out.println("Running TED with BBF completed.");
				}
			} else {
				// wrong first argument
				throw new IllegalArgumentException("\nProvide correct first argument.");
			}
		} catch (Exception ex) {
			System.out.println("Error occured during the execution of program:");
			ex.printStackTrace();
		}
		System.out.println("Done.");
		// compareTED_Depth_Flat_Tree();
	}

	public static Hashtable<String, Integer> compareHashtablesWithTreesTED(
			Hashtable<Integer, Tree> treetable1, Hashtable<Integer, Tree> treetable2) {
		int goodFind = 0;
		int numberOfComparisons = 0;
		Hashtable<String, Integer> result = new Hashtable<String, Integer>();
		System.out.println(String.format(
				"Comparing forests of [%s] and [%s] trees using TED", treetable1.size(),
				treetable2.size()));

		long time = System.currentTimeMillis();
		System.out.println("comparison started... ");
		int temp;
		Tree tree1;
		Tree tree2;
		HashSet<Integer> assignedIdsFromTree2 = new HashSet<Integer>();

		// for all the trees in first tree
		for (Entry<Integer, Tree> pair1 : treetable1.entrySet()) {
			int bestMatchValue = Integer.MAX_VALUE;
			int bestMatchID = 0;
			// get the tree
			tree1 = pair1.getValue();

			// try all trees in the second tree
			for (Entry<Integer, Tree> pair2 : treetable2.entrySet()) {
				// except the ones which have already been assigned
				if (assignedIdsFromTree2.contains(pair2.getKey()))
					continue;

				// get the tree
				tree2 = pair2.getValue();

				// call the algorithm!
				temp = TedAlgorithm.getDistance(tree1, tree2);

				numberOfComparisons++;
				if (temp < bestMatchValue) {
					bestMatchValue = temp;
					bestMatchID = pair2.getKey();
					if (bestMatchValue == 0) {
						goodFind++;
						assignedIdsFromTree2.add(bestMatchID); // pair2.getKey()
						result.put(pair1.getKey() + ":" + bestMatchID, bestMatchValue);
						break;
					}
				}
			}

			// match found
			if (bestMatchValue != Integer.MAX_VALUE && bestMatchID != 0) {
				assignedIdsFromTree2.add(bestMatchID);
				result.put(pair1.getKey() + ":" + bestMatchID, bestMatchValue);
			}
		}
		System.out.println(String.format("comparison finished! (%s times in %s ms)",
				numberOfComparisons, (System.currentTimeMillis() - time)));

		System.out.println("Good finds (two entirely equal trees found): " + goodFind);
		return result;
	}

	public static Hashtable<String, Integer> compareHashtablesWithTreesBBF(
			Hashtable<Integer, Tree> treetable1, Hashtable<Integer, Tree> treetable2,
			double treshold) {
		int goodFind = 0;
		int numberOfComparisons = 0;
		Hashtable<String, Integer> result = new Hashtable<String, Integer>();
		System.out
				.println(String
						.format("Comparing forests of [%s] and [%s] trees using BBF+TED with threshold %s",
								treetable1.size(), treetable2.size(), treshold));

		long time = System.currentTimeMillis();
		System.out.println("comparison started... ");
		int temp;
		Tree tree1;
		Tree tree2;
		HashSet<Integer> assignedIdsFromTree2 = new HashSet<Integer>();
		// for all the trees in first tree
		for (Entry<Integer, Tree> pair1 : treetable1.entrySet()) {
			int bestMatchValue = Integer.MAX_VALUE;
			int bestMatchID = 0;
			// get the tree
			tree1 = pair1.getValue();

			// try all trees in the second tree
			for (Entry<Integer, Tree> pair2 : treetable2.entrySet()) {
				// except the ones which have already been assigned
				if (assignedIdsFromTree2.contains(pair2.getKey()))
					continue;

				// get the tree
				tree2 = pair2.getValue();

				temp = BBFAlgorithm.getDistance(tree1, tree2, treshold);

				numberOfComparisons++;
				if (temp < bestMatchValue) {
					bestMatchValue = temp;
					bestMatchID = pair2.getKey();
					if (bestMatchValue == 0) {
						goodFind++;
						assignedIdsFromTree2.add(bestMatchID); // pair2.getKey()
						result.put(pair1.getKey() + ":" + bestMatchID, bestMatchValue);
						break;
					}
				}
			}

			// match found
			if (bestMatchValue != Integer.MAX_VALUE && bestMatchID != 0) {
				result.put(pair1.getKey() + ":" + bestMatchID, bestMatchValue);
			}
		}
		System.out.println(String.format("comparison finished! (%s times in %s ms)",
				numberOfComparisons, (System.currentTimeMillis() - time)));

		System.out.println("Good finds (two entirely equal trees found): " + goodFind);
		return result;
	}

	/**
	 * Print the usage of the application UI in case user inputs incorrect
	 * parameters or inputs -h or --help.
	 */
	public static void printUsage() {
		System.out
				.println("\nUsage of treeedit (note that the first argument must be -h, -g, -r, -t or -b):"
						+ "\n-h or --help\n\tprint this message"
						+ "\n-g or --gen\n\t-o <output_file> \n\t-f <fanout> \n\t-l <layers> "
						+ "\n\t-s <label_length> \n\t-n <number_of_trees>"
						+ "\n\tgenerate forest of trees with given parameters"
						+ "\n-r or --rand\n\t-i <input_file> \n\t-c <chance> \n\t-o <output_file>"
						+ "\n\trandomize tree and output it "
						+ "\n-t or --ted\n\t-i <input_file_1> <input_file_2> \n\t[-o <output_file>]"
						+ "\n\tcall tree-edit-distance"
						+ "\n-b or --bbf\n\t-i <input_file_1> <input_file_2> \n\t-t <treshold>\n\t[-o <output_file>]"
						+ "\n\tcall tree-edit-distance with BBF and given threshold");
	}
}
